package storageManager;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;


public class DataFile extends FileClass{
	public TableRecord tableRecord;//Table record of the data file
	public DataFile(TableRecord tableRecord) throws IOException{
		this.tableRecord = tableRecord;
//		this.raf = new RandomAccessFile(tableRecord.getTableName()+"dat.txt", "rw");
		this.raf = new RandomAccessFile(tableRecord.getTableName()+".dat", "rw");
		if(size()==0){//If the data file is just created constructor creates a page. 
			createAPage();
		}
	}
	public void createAPage() throws IOException{//This method creates a new page in the data file.
		int offset = numberOfPages()*512;
		raf.seek(offset);
		raf.write(tableRecord.getNumberOfTotalDataRecord());//Writes the page header.
	}
	public int firstAvailablePage() throws IOException{//This method checks the page headers of every pages and returns the number of first page that has available place in it.   
		int pageNumber = numberOfPages();
		for(int i=0; i<pageNumber; i++){
			raf.seek(i*512);
			int numOfAvailableDataRecords = raf.read();//Reads the page header that tells the number of available space in that page
			if(numOfAvailableDataRecords>0){//If this page has available place,
				return i;//Returns the page number
			}
		}
		createAPage();//If there is not any available space in all pages of the file, it creates a new page,
		return pageNumber;//And return the number of that page
	}
	public ArrayList<DataRecord> listOfAllDataRecords(ArrayList<IndexRecord> indexRecords) throws IOException{//Takes an arraylist that holds index records.
		ArrayList<DataRecord> result = new ArrayList<DataRecord>();
		for(IndexRecord indexRecord:indexRecords){
			result.add(findDataRecord(indexRecord));//Adds every data record that the index records in the given arraylist correspond to.
		}
		return result;//And returns the arraylist that holds those data records
	}
	public DataRecord findDataRecord(IndexRecord indexRecord) throws IOException{//This method finds and returns the data record that the given index record corresponds.
		int pageNumber = indexRecord.getPageNumber();
		int recordNumber = indexRecord.getRecordNumber();
		DataPage thePage = byteToPage(pageNumber);
		return thePage.findDataRecord(recordNumber);
	}
	public ArrayList<Integer> addDataRecord(ArrayList<String> values) throws IOException{//Takes an arraylist of strings as parameter
		ArrayList<Integer> result = new ArrayList<Integer>();//The arraylist to be returned
		DataRecord dataRecord = new DataRecord(1,values);//Creates a data record with the given arraylist of strings
		int pageNumber = firstAvailablePage();//Finds the page to put the data record
		DataPage thePage = byteToPage(pageNumber);//Opens that data page
		int recordNumber = thePage.addDataRecord(dataRecord);//Adds the data record to available place and gets the record number of the added data record
		writeThePage(pageNumber, thePage);//Writes the page back
		result.add(pageNumber);//Adds the page number to the arraylist
		result.add(recordNumber);//Adds the record number to the arraylist
		return result;//Returns the arraylist
	}
	public void deleteARecord(ArrayList<Integer> place) throws IOException{//This method logically delete the data record in the given place
		int pageNumber = place.get(0);
		int recordNumber = place.get(1);
		DataPage thePage = byteToPage(pageNumber);//Gets the data page that the record to be deleted
		thePage.deleteARecord(recordNumber);//Logically deletes the record
		writeThePage(pageNumber, thePage);//Writes the page back to file
	}
	@Override
	public DataRecord byteToRecord(byte[] bytes){//Takes a byte array as parameter and parse it to a data record object and returns it 
		DataRecord result = new DataRecord();
		ArrayList<String> values = new ArrayList<String>();
		result.setStatus(bytes[0]);
		int offset = 1;
		for(int i=0;i<tableRecord.getNumberOfFields();i++){
			int size = tableRecord.getFieldSizes().get(i);
			byte[] temp = new byte[size];
			System.arraycopy(bytes, offset, temp, 0, size);
			offset += size;
			values.add(Tools.byteToString(temp));
		}
		result.setValues(values);
		return result;
	}
	@Override
	public DataPage byteToPage(int pageNumber) throws IOException{//Takes the number of page as parameter and returns that page as a page object
		DataPage result = new DataPage();
		ArrayList<DataRecord> listOfRecords = new ArrayList<DataRecord>();
		int offset = pageNumber*512;
		byte[] bytes = readThePage(pageNumber);
		result.setNumberOfAvailableRecords(bytes[0]);
		for(int i=0;i<tableRecord.getNumberOfTotalDataRecord();i++){
			byte[] recordInBytes = new byte[tableRecord.getSizeOfADataRecord()];
			System.arraycopy(bytes, 1+i*tableRecord.getSizeOfADataRecord(), recordInBytes, 0, recordInBytes.length);
			DataRecord record = byteToRecord(recordInBytes);
			if(record.getStatus() == 1 || record.getStatus() == 2){
				listOfRecords.add(record);
			}
		}
		result.setDataRecords(listOfRecords);
		return result;
	}
	public void writeThePage(int pageNumber,DataPage thePage) throws IOException{//Writes the page object into the file 
		raf.seek(pageNumber*512);
		raf.write(thePage.toByte(tableRecord));
	}
	public byte[] readThePage(int pageNumber) throws IOException{//Returns the page object of the file with given page number 
		byte[] result = new byte[512];
		raf.seek(pageNumber*512);
		raf.read(result);
		return result;
	}
}
